#define DEBUG
#include "tracker.h"
struct node *head=NULL; 
static int Test=0;

 //struct file_part *fhead=NULL;
/******************************Functions definations**************************************/

/* This function just makes Initial 1st time (peer_count = 1), DS popuate for each file, 
*  remaining all updates for each part are handled by update function 
*/

void append_file_part(struct node **new1, char *fname, int seed_id,int part_number, int myport, char IP_addr[IPv4_SIZE])
{
    struct node *temp;
    struct file_part *new_temp, *r;
    char * f_name;
    int peer_count = 0;

    //seed_id = peer_id;

   

    temp = *new1 ;
	
    f_name = temp->value;
		//printf("\n Inside Append file part with file name: %s ",f_name);
		fflush(stdout);  
	while ( strcmp((char *)fname, (const char*)f_name)!= 0)
	{
		temp = temp->next;
		f_name = temp->value;
		if(temp->next == NULL && (strcmp((char *)fname, (const char*)f_name) != 0 ))
		{
			printf("\n Sorry ! File not present in the file_list. Needs to be added ");
			
			exit(1);
		}
	}
	printf("\n After Compare:%s ",temp->value);
		fflush(stdout);  
	peer_count = 1;	  
	if( temp->node_part == NULL)
	{       
               	new_temp = (struct file_part *)malloc ( sizeof ( struct file_part) ) ;

		new_temp-> next = NULL;                                                 
		new_temp-> part_num = part_number;

                new_temp-> seeder_list[peer_count].seeder_id = seed_id;
                new_temp-> seeder_list[peer_count].sd_sin.sin_port = htons(myport) ; // ????
		inet_pton(AF_INET,IP_addr,&(new_temp-> seeder_list[peer_count].sd_sin.sin_addr));
                new_temp-> seeder_list[peer_count].sd_sin.sin_family = AF_INET ;

                /*
  			Few more fields to put.. like dwld status n all .......
     		*/


		temp->node_part = new_temp;
		//printf("\n Inside append_file_part : %s , of part number %d , seeder_IP %s and Port ", temp->value, new_temp-> part_num,myport);
     	}

	else
	{
		new_temp = temp->node_part ;
		while ( new_temp -> next != NULL ) 
		new_temp = new_temp -> next ;

		r = (struct file_part *)malloc ( sizeof ( struct file_part ) ) ;
		r-> next = NULL;
		r-> part_num = part_number;
                r-> seeder_list[peer_count].seeder_id = seed_id;
                r-> seeder_list[peer_count].sd_sin.sin_port = htons(myport) ;
                inet_pton(AF_INET,IP_addr,&(r-> seeder_list[peer_count].sd_sin.sin_addr));
                r-> seeder_list[peer_count].sd_sin.sin_family = AF_INET ;	

                 /*
  			Few more fields to put.. like dwld status n all .......
     		*/

		inet_ntop(AF_INET,&(r-> seeder_list[peer_count].sd_sin.sin_addr),IP_addr,IPv4_SIZE); // presentation					 		                           
		new_temp-> next = r;
		printf("\n Inside append_file_part : \t%s, \t%d , \t%s, \t%d", temp->value, r-> part_num, IP_addr, ntohs(r-> seeder_list[peer_count].sd_sin.sin_port));

	}
}

/****************** append function *************************************************/


void append_list ( struct node ** new2, char * name )
{
struct node *temp, *r ;
   static int i=1;
	if ( *new2 == NULL )  // if the list is empty, create first node
    {
		printf("There is no file present , at start");
		fflush(stdout);
        temp = (struct node *)malloc ( sizeof ( struct node) ) ;
	    temp -> value = name ;
        temp -> next = NULL ;
        temp -> node_part = NULL;
        *new2 = temp ;
		printf("\n Inside append_list ( 1st Node ): %s ", temp->value);
		fflush(stdout);
		i++;
    }
    else
    {   		  
	temp = *new2 ;

	while ( temp  != NULL ) // go to last node 
	{	
		temp = temp -> next ;
		printf("Incrementing node in appendix list %d",i);
       fflush(stdout);
		i++;				
	}
	//printf("\n Appending ith %d at next location current location : %s ", i,temp->value);
	fflush(stdout);
	r = (struct node *)malloc ( sizeof ( struct node ) ) ; // add node at the end 
	r -> value = name ;
	r -> next = NULL ;
	r -> node_part = NULL;
	temp = r ;
	printf("\n Inside append_list : %s ", r->value);
	fflush(stdout);
	

    } 
}

/********************* Update function **************************/

void Update ( struct node **new1, char *fname, char * protocol, int p_id, int part_number, int myport, char IP_addr[IPv4_SIZE]) // 7 parameters
{
    struct node *temp;
    struct file_part *new_temp, *r;
    char * f_name, protocol_code;
    int i= 0;

    int peer_count ;  
    temp = *new1 ;

    protocol_code = protocol;
	
	
    f_name = temp->value;
		  
	while ( strcmp((char *)fname, (const char*)f_name)!= 0)
	{
		temp = temp->next;
		f_name = temp->value;
		if(temp->next == NULL && (strcmp((char *)fname, (const char*)f_name) != 0 ))
		{
			printf("\n Sorry ! File not present in the file_list. Needs to be added ");
			exit(1);
		}
	}

        new_temp = temp->node_part ;

	for ( i = 1 ; i < part_number ; i++ )
	new_temp = new_temp -> next ;

	if ( new_temp == NULL )
	{
		printf ( "\nThere are less than %d elements in list", part_number ) ;
		return ;
	}
    
	if ( new_temp->part_num == part_number)
	{        
            
		
             if ( protocol_code == 0x01) // seeder update
	     {
                for (peer_count=0;peer_count< MAX_PEERS;peer_count++) // Reach 
                {
		  if(new_temp->seeder_list[peer_count].seeder_id == NULL) ; // Initialise somewhere , this array as null. = {0};
                  break ; 
	        }
		new_temp-> seeder_list[peer_count].seeder_id = p_id;
		new_temp-> seeder_list[peer_count].sd_sin.sin_port = htons(myport) ; 
                //new_temp-> seeder_list[peer_count].sd_sin.sin_addr.s_addr = IP_addr ;
                inet_pton(AF_INET,IP_addr,&(new_temp-> seeder_list[peer_count].sd_sin.sin_addr));
                new_temp-> seeder_list[peer_count].sd_sin.sin_family = AF_INET ;
	     }

             else  // leecher update
	     {
                 for (peer_count=0;peer_count< MAX_PEERS;peer_count++) // Reach 
                 {
		   if(new_temp->leecher_list[peer_count].leecher_id == NULL ); // Initialise somewhere , this array as null. = {0};
                   break ; 
	         }
                 new_temp-> leecher_list[peer_count].leecher_id = p_id;
		 new_temp-> leecher_list[peer_count].lc_sin.sin_port = htons(myport) ; // ???? or just myport;
                 //new_temp-> leecher_list[peer_count].lc_sin.sin_addr.s_addr = IP_addr ;
                 inet_pton(AF_INET,IP_addr,&(new_temp-> leecher_list[peer_count].lc_sin.sin_addr));
                 new_temp-> leecher_list[peer_count].lc_sin.sin_family = AF_INET ;
	     }
          

		// new_temp->percent_downloaded = 99.900000; //               ???? reqd ?? when n w.r.t to whom ?? think ..
		//new_temp->seeder_list[1].seeder_id = peer_id;
                     
		
	}

       //printf("\n\n Percentage downloaded for part number %d is %f ",part_number, new_temp->percent_downloaded);
       printf("\n\n Its peer id is : %d \n\n ",new_temp->seeder_list[1].seeder_id);

}

void print_list(struct node *head)
{
	struct node * temp=head;
    struct file_part * horizontal;
	int i=0;
	char pipbuff[IPv4_SIZE]={0};

	if (temp != NULL)
    { 
    	//temp->value;
    	//printf("\n Head value %s",temp->value);
	while(temp!=NULL)
	{
		printf("\n ***********Head value %s********************",temp->value);
		fflush(stdout);
		if(temp->node_part==NULL)
			printf("\n The horizontal node is NULL");
  /*      if ( temp->node_part != NULL)
		{
        	horizontal = temp->node_part;
	  		while ( horizontal != NULL)
        	{ 
 
		        printf("\n***The Data in part list of file is:***");
    	        printf("\nPart number: %d",horizontal->part_num);
       
    		    printf("\n Seeder Info:\n");
    		    for(i=0;i<MAX_SEEDS;i++)
        		{
            		//************************************* Change the formatting specifiers according to length
            		printf("\tID:%5d", horizontal->seeder_list[i].seeder_id);
            		inet_ntop(AF_INET, &(horizontal->seeder_list[i].sd_sin.sin_addr), pipbuff, IPv4_SIZE);
            		printf("  Seeder ip is :%16s",pipbuff);    
            		memset(&pipbuff,0,IPv4_SIZE);
            		printf(" | ");
            		if((i%3)==2)
					{
            			printf("\n");
            		}
        		}
        		printf("\n Leecher Info:\n");
		        for(i=0;i<MAX_LEECHES;i++)
    		    {
        		    printf("\tID:%5d", horizontal->leecher_list[i].leecher_id);
            		inet_ntop(AF_INET, &(horizontal->leecher_list[i].lc_sin.sin_addr), pipbuff, IPv4_SIZE);
            		printf("  Leecher ip is :%16s",pipbuff);    
            		memset(&pipbuff,0,IPv4_SIZE);
           			printf(" | ");
            		if((i%3)==2)
					{
            			printf("\n");
           		 	}
        		}
        
        		horizontal=horizontal->next; // go to next horizontal node (FILE pART)
        		printf("************\n");
    		} //end of while
    
	     }
	*/
		temp=temp->next;
		printf("*********************************************************************************************\n");
		fflush(stdout);		
		}//end of while vertical
	}
}


/************************************ Read_DS_and_send *******************************/

char* Read_DS ( struct node **new1, int seq_no,char *fname, int part_number )
{
    struct node *temp;
    struct file_part *new_temp, *r;
    char * f_name;
    int i= 0;
    char send_buffer[100];
    char token3[100];
    int peer_id, port_no;
    char IP_addr[IPv4_SIZE];
    char* reply_protocol = "0x04";

    int ack_no = seq_no +1;
    temp = *new1 ;
	
    printf("\n Inside Read DS .... \n"); fflush(stdout);
    f_name = temp->value;
    printf("\n f_value %s, temp->value %s \n", f_name, temp->value); fflush(stdout);
		  
	while ( strcmp((char *)fname, (const char*)f_name)!= 0)
	{
		temp = temp->next;
		f_name = temp->value;
                printf("\n inside while ....");
               
		if(temp->next == NULL && (strcmp((char *)fname, (const char*)f_name) != 0 ))
		{
			printf("\n Sorry ! File not present in the file_list. Needs to be added ");
			//getch();
			exit(1);
		}
          
	}

	printf("\n Inside Read DS f_name is matched temp->value = %s",temp->value); fflush(stdout);
        
        new_temp = temp->node_part ;
	for ( i = 1 ; i < part_number ; i++ )
	new_temp = new_temp -> next ;

	if ( new_temp == NULL )
	{
		printf ( "\nThere are less than %d elements in list", part_number ) ;
		return ;
	}
         
	if ( new_temp->part_num == part_number)  // Need to read always seeder info .. reqd for seeding.
	{   
	  printf("\n Peer_id read from DS is %d",new_temp->seeder_list[1].seeder_id);
	  peer_id = new_temp->seeder_list[1].seeder_id;
          port_no = ntohs(new_temp->seeder_list[1].sd_sin.sin_port);        // ????? ???????????????? ok ??
	  // IP_addr = new_temp->seeder_list[1].sd_sin.sin_addr.s_addr; //  ????????????????????   ok ??
          inet_ntop(AF_INET,&(new_temp->seeder_list[1].sd_sin.sin_addr),IP_addr,IPv4_SIZE); // presentation 
                     
	}
     
	//sprintf(send_buffer,"%s$%d$%s$%d$%d$%s",reply_protocol,ack_no,fname,peer_id,port_no,IP_addr); // is protocol code %s or %u or %d ?? also IP YES ??
                 
         sprintf(send_buffer,"%s$%d$%s$%d",reply_protocol,ack_no,fname,peer_id); // FILL MORE ....

         printf("\n\n send buffer is : %s\n", send_buffer);
         return send_buffer; 

}


int main(void)
{
	int alen,t;
        char IP_addr[IPv4_SIZE]; 

        pthread_t	new_thread[NUMBER_OF_THREADS];
	pthread_attr_t	thread_attr;

		
	/******************** Proxy_Server data structures *****************/
	
	struct sockaddr_in tracker_sin;	/* Proxy server endpoint   */
	struct sockaddr_in client_sin;	    	/* an SMTP Client endpoint */
	struct servent	*tracker_pse;	        /* pointer to port, currently not used in code */
	int	msocket, ssocket;		/* socket descriptor and socket type */

	/********************************************************************/

	
	memset(&tracker_sin,0,sizeof(tracker_sin) );
	memset(&client_sin,0,sizeof(client_sin) );
	printf("\n Starting my Sparta Torrent Tracker ...\n");
	
				
	/************************P_Server side ******************************************/

      	tracker_sin.sin_family = AF_INET;
	tracker_sin.sin_addr.s_addr = INADDR_ANY;
        tracker_sin.sin_port = htons(TRACKER_PORT);
	
	
	/*Create TCP master Socket for Proxy Server*/
	msocket = socket(PF_INET, SOCK_STREAM,0);
	if (msocket < 0)
		printf("\n Cannot create the socket ");

       /* Bind the master socket */
	if (bind(msocket, (struct sockaddr *)&tracker_sin, sizeof(tracker_sin)) < 0)
		printf("\n ERROR ! Cannot bind the socket ");

    	if (listen(msocket, 3) < 0)
		printf("\n ERROR ! Cannot listen the socket ");
		
	while (1) 
	{
		printf(" \n Inside while: Waiting for connections\n");
		printf(" \n Creating New Thread ..\n");
		
	       (void) pthread_attr_init(&thread_attr);
	       (void) pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
                
	        for(t=0;t<NUMBER_OF_THREADS;t++) 
	        {
		  alen = sizeof(client_sin);
                  printf("\n Waiting at accept call ....");
		  fflush(stdout);
		  ssocket = accept(msocket, (struct sockaddr *)&client_sin, &alen);
                  
		  printf("\n Socket accepted .... ");
		  fflush(stdout);
                
		  if (ssocket < 0)
			{
				if (errno == EINTR)
					continue;
				printf("accept: %s\n", strerror(errno));
			}
                

              	   thread_data_array[t].ssocket = ssocket;
                 //  thread_data_array[t].seeder_list[t].sd_sin.sin_port = client_sin.sin_port ;
		   thread_data_array[t].client_sin.sin_addr.s_addr = client_sin.sin_addr.s_addr ; 
                   thread_data_array[t].client_sin.sin_family = AF_INET ;

                  #ifdef DEBUG
                    inet_ntop(AF_INET,&(thread_data_array[t].client_sin.sin_addr),IP_addr,IPv4_SIZE); // presentation
                    printf("\n its IP is : %s ", IP_addr);
                    fflush(stdout);
                  #endif
              if(head!=NULL)
				{
					printf("\n Very Initial Value of Head is %s",head->value);					
					fflush(stdout);
				}    
                   if (pthread_create(&new_thread[t], &thread_attr,communicate,(void *)&thread_data_array[t]) < 0)
		   printf("\n ERROR ! Cannot Create new thread. ");
                   
                   printf("\n Before passing to function ....  ssocket is : %d, ... %d", thread_data_array[t].ssocket, ssocket);
			        
               } 

	}   // while end
}
 
void * communicate(void * thread_arguement)
{
 
	int r, i ,buff_len;
  char buff[BUF_SIZE]={0}, cp[BUF_SIZE]={0};
  char *filename;
  unsigned char * protocol_feild;
  unsigned int protocol=9;


  const char delimiter[] = "$";
	struct sockaddr_in cli_sin; 
  char *no_of_parts, *peer_id, *port_num, *part_no_available, *seeder_id, *leecher_id , *seq_no, *send_buffer;
  int total_parts, seed_id, port_here, part_available, s_id, l_id, sequence_no ;
 
  struct thread_data *threaddata;
	
  char IP_address[IPv4_SIZE]; 
  int bufflen=BUF_SIZE;	

  int new_socket;
threaddata = (struct thread_data*)thread_arguement;
	new_socket=threaddata->ssocket;
	
	cli_sin.sin_addr.s_addr=threaddata->client_sin.sin_addr.s_addr;        
//memcpy(&cli_sin,threaddata->client_sin,sizeof(threaddata->client_sin.sin_addr));

  printf("\n Inside communicate function ");
  printf("\n Inside function ....  ssocket is : %d", new_socket);
  fflush(stdout);
  if(Test>0)
				{
					printf("\n The initial Value of Head is %s",head->value);					
					fflush(stdout);
				}    

  memset(&buff,0,BUF_SIZE);
 inet_ntop(AF_INET,&(cli_sin.sin_addr),IP_address,IPv4_SIZE); // presentation

#ifdef DEBUG
  printf("\n IP, passed inside function is : %s ", IP_address);
  fflush(stdout);
#endif
    
  
   /* start reading buffer for Communication Protocol */
   printf("\n Now reading the buffer ......");
   fflush(stdout);

   r = read(new_socket,buff, bufflen-1);
   if(r<0){
	printf("\n Error in Reading from Peers");
	}
   printf("\n Read data ... r is:%d",r);
	fflush(stdout);
   buff[r]='\0';
   printf("\nReceived Msg :%s",buff);
	printf("\n Received Buffer , Now copying input to other buffer => tokenizing..");    
	fflush(stdout);
	printf("\n buffer is %s", buff);
        fflush(stdout);

   strcpy((char*)cp,(const char *)buff);
   printf("\n before protocol.......... : %s",cp);
   fflush(stdout);

   protocol_feild = strtok(buff,delimiter);
 //  strncpy(&protocol, protocol_feild, 1);
   protocol = atoi(protocol_feild);
	printf("\n Buffer %s copied.. received token: %x",buff,protocol);    
	fflush(stdout);
   switch (protocol)
   {
        case 0x00: // 1st time populate DS. Msg format :  [ protocol $ filename $ no_of_parts $ peer_id $ port_num ] // Always by Seeder
                   // No Reply send from tracker
 #ifdef DEBUG
		    printf("\n Inside protocol 0x00");
		   fflush(stdout);
#endif

                   filename = strtok(NULL, delimiter);
                   no_of_parts = strtok(NULL, delimiter);
                   //total_parts = atoi(no_of_parts);
                   sscanf (no_of_parts, "%d", &total_parts); 
                
                   peer_id = strtok(NULL, delimiter);
                   //seeder_id = atoi (peer_id);
                   sscanf (peer_id, "%d", &seed_id); 

		   port_num = strtok(NULL, delimiter);
                   sscanf (port_num, "%d", &port_here); 
				if(Test>0)
				{
					printf("\n The Start Value of Head(before append) is %s",head->value);					
					fflush(stdout);
				}                
				   append_list(&head,filename);
                   printf("\n check 123 ....\n");
					printf("\n The Start Value of Head is %s",head->value);					
					Test++;
					fflush(stdout);                   
				for(i=1 ;i <= total_parts;i++)
         		   {
		 			append_file_part( &head, filename, seed_id, i , port_here, IP_address); // IP & port need to taken from struct client_sin ( 6 parameter pass)
				   }	
				printf("\n The Start Value of Head is %s",head->value);					
				fflush(stdout);		
			print_list(head);  
			printf("\n The Start Value of Head is %s",head->value);					
				fflush(stdout);                 
 			break;
        case 0x01: // Seeder Update. Msg format : [ protocol $ filename $ part_no_available $ seeder_id $ port_num ]
		   // No Reply send from tracker
 #ifdef DEBUG
		    printf("\n Inside protocol 0x01");
		    fflush(stdout);
#endif
                   filename = strtok(NULL, delimiter);
                   part_no_available = strtok(NULL, delimiter);
                   //part_available = atoi(part_no_available);
		   sscanf (part_no_available, "%d", &part_available); 
 
                   seeder_id = strtok(NULL, delimiter);
                   //s_id = atoi (seeder_id);
                   sscanf (seeder_id, "%d", &s_id); 

                   port_num = strtok(NULL, delimiter);
                   sscanf (port_num, "%d", &port_here); 
                   
		   Update( &head, filename, protocol, s_id, part_available, port_here, IP_address); //  7 parameters passed
			break;
                 
        case 0x02: // Leecher Update. Msg format : [ protocol $ filename $ part_no_available $ leeher_id $ port_num ]
                   // No reply send from tracker
   
#ifdef DEBUG
		    printf("\n Inside protocol 0x02");
		   fflush(stdout);
#endif

		   filename = strtok(NULL, delimiter);
                   part_no_available = strtok(NULL, delimiter);
                   //part_available = atoi(part_no_available);
                   sscanf (part_no_available, "%d", &part_available); 
 
                   leecher_id = strtok(NULL, delimiter);
                   //l_id = atoi (leecher_id);
                   sscanf (leecher_id, "%d", &l_id); 

		   port_num = strtok(NULL, delimiter);
                   sscanf (port_num, "%d", &port_here); 
                 
                   Update( &head, filename, protocol, s_id, part_available, port_here, IP_address); //  7 parameters passed
                   break;


        case 0x03: // Reterive info request. Msg format : [ protocol $ seq_no $ filename $ no_of_parts ]  // Always by leecher
        // Reply from the tracker. Reply format : [ protocol $ ack_no [seq_no + 1] $ filename $ seeder_id $ port_no $ IP ] here ip n port will be read from DS

#ifdef DEBUG
		   printf("\n Inside protocol 0x03");
		   fflush(stdout);
#endif
                  seq_no = strtok(NULL, delimiter);
                  //sequence_no = atoi (seq_no);
                  sscanf (seq_no, "%d", &sequence_no);
                  printf("\n debug -1 ... %d",sequence_no); fflush(stdout);

		   filename = strtok(NULL, delimiter);
                   printf("\n debug -1 ... %s",filename); fflush(stdout);

                   no_of_parts = strtok(NULL, delimiter);
		   // total_parts = atoi(no_of_parts);
		   sscanf (no_of_parts, "%d", &total_parts);
                   printf("\n debug -2 ... %d", total_parts); fflush(stdout);

                  for(i=0;i<total_parts;i++)
		  {
                     printf("\n debug -3"); fflush(stdout);
            
                    // send_buffer = Read_DS(&head,filename, sequence_no,i); 
                    Read_DS(&head,filename, sequence_no,i); 
                      printf("\n debug -4"); fflush(stdout);
                   //send_buffer='\0';

                   r = strlen(send_buffer);
	           printf("Size of buffer %s is :  %d",send_buffer,r);

	           if ( write(new_socket,send_buffer,r)<0)
		   printf("\n Error ! Wriritng to peer Server socket failed ");
                   break;
		 }

           }// Switch ends
	printf("\n\n Exiting Thread Now ....... \n\n");
        fflush(stdout);
	pthread_exit(NULL);
}

